﻿// ===================================
// <copyright>LogoUI Co.</copyright>
// <author>Vlad Spivak</author>
// <email>mailto:vlads@logoui.co.il</email>
// <created>20/00/10</created>
// <lastedit>Thursday, November 25, 2010</lastedit>

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the 'Software'), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
//
// <remarks>Part of this software based on various internet sources, mostly on the works
// of members of Wpf Disciples group http://wpfdisciples.wordpress.com/
// Also project may contain code from works of Nito and OpenLight group</remarks>
// ====================================================================================//
#if SILVERLIGHT
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.ReflectionModel;
using System.Linq;
using System.Windows.Markup;
using LogoFX.Composition;
using LogoFX.Core;
using MefContrib.Hosting.Generics;
using Microsoft.ComponentModel.Composition.Hosting;

namespace Caliburn.Micro
{
    /// <summary>
    /// <c>Bootstrapper</c> that uses simple container
    /// </summary>
    /// <typeparam name="T"></typeparam>
    [ContentProperty("CatalogConfig")]
    public class SimpleBootstrapper<T> : BootstrapperBase<T>
    {
        private SimpleContainer _container;
        private object _defaultLifetimeScope;
        readonly DeploymentCatalogLoader _loader = new DeploymentCatalogLoader();
        readonly DeploymentCatalog _dc = new DeploymentCatalog();



        /// <summary>
        /// Override to configure the framework and setup your <c>IoC</c> container.
        /// </summary>
        protected override void Configure()
        {
            base.Configure();
            //configure mef
            _loader.CatalogCached += (unused, args) => args.Catalog.Parts
                                                         .Select(part => ReflectionModelServices.GetPartType(part).Value.Assembly)
                                                         .Where(assembly => !AssemblySource.Instance.Contains(assembly))
                                                         .Apply(a => AssemblySource.Instance.Add(a));

            CompositionHostEx.Initialize(_loader.LoadCatalogConfigAsync(new GenericCatalog(_dc, new GenericContractRegistry())));

            
        }

        /// <summary>
        /// Gets or sets the catalog configuration.
        /// </summary>
        /// <value>The catalog configuration.</value>
        public DeploymentCatalogConfig CatalogConfig
        {
            get { return _loader.CatalogConfig; }
            set { _loader.CatalogConfig = value; }
        }


        /// <summary>
        /// Override this to add custom behavior to execute after the application starts.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The arguments.</param>
        protected override void OnStartup(object sender, System.Windows.StartupEventArgs e)
        {
            if (CatalogConfig != null)
            {
                EventHandler loadAction = null;
                loadAction = (s, a) =>
                {
// ReSharper disable AccessToModifiedClosure
                    _loader.ConfigLoaded -= loadAction;
// ReSharper restore AccessToModifiedClosure
                    SimpleContainerRegister();
                    Execute.OnUIThread(DisplayRootView);
                };

                _loader.ConfigLoaded += loadAction;
                DeploymentCatalogLoader.ConfigLoadHelper loadHelper = new DeploymentCatalogLoader.ConfigLoadHelper(CatalogConfig);
                loadHelper.LoadAsync(_loader.OnLoaded, _loader.OnGroupLoaded, _loader.OnItemLoaded);
            }
            else
            {
                SimpleContainerRegister();
                base.OnStartup(sender, e);
            }
        }

        /// <summary>
        /// Called on configure.
        /// </summary>
        /// <param name="container">The container.</param>
        protected virtual void OnConfigure(SimpleContainer container)
        {
            container.RegisterSingleton(typeof(IWindowManager), null, typeof(WindowManager));
            container.RegisterSingleton(typeof(IEventAggregator), null, typeof(EventAggregator));
        }

        /// <summary>
        /// Gets the current lifetime scope.
        /// </summary>
        /// <value>The current lifetime scope.</value>
        protected virtual object CurrentLifetimeScope
        {
            get { return _defaultLifetimeScope ?? (_defaultLifetimeScope = new Object()); }
        }


        #region Overrides
        /// <summary>
        /// Override this to provide an <c>IoC</c> specific implementation.
        /// </summary>
        /// <param name="service">The service to locate.</param>
        /// <param name="key">The key to locate.</param>
        /// <returns>The located service.</returns>
        protected override object GetInstance(Type service, string key)
        {
            return _container.GetInstance(service, key);
        }


        /// <summary>
        /// Override this to provide an <c>IoC</c> specific implementation
        /// </summary>
        /// <param name="service">The service to locate.</param>
        /// <returns>The located services.</returns>
        protected override IEnumerable<object> GetAllInstances(Type service)
        {
            return _container.GetAllInstances(service);
        }

        /// <summary>
        /// Override this to provide an <c>IoC</c> specific implementation.
        /// </summary>
        /// <param name="instance">The instance to perform injection on.</param>
        protected override void BuildUp(object instance)
        {
            _container.BuildUp(instance);
        }

        #endregion

        /// <summary>
        /// Gets or sets the modules.
        /// </summary>
        /// <value>The modules.</value>
        [ImportMany(typeof(ISimpleModule))]
        public ISimpleModule[] Modules { get; set; }

        #region private implementation

        /// <summary>
        /// Configures this instance.
        /// </summary>
        private void SimpleContainerRegister()
        {
            CompositionHostEx.GetContainer().SatisfyImportsOnce(this);

            _container = new SimpleContainer();
            _container.RegisterSingleton(typeof(T), null, typeof(T));
            _container.RegisterInstance(typeof(SimpleContainer), null, _container);

            RegisterModules();

            //comment this if you not want views and viewmodels registration
            RegisterViewsAndViewModels();

            OnConfigure(_container);
        }

        private void RegisterModules()
        {
            if (Modules != null)
                Modules.Apply(a => a.RegisterModule(_container, () => CurrentLifetimeScope));
        }

        private void RegisterViewsAndViewModels()
        {
            //  register view models
            AssemblySource.Instance.ToArray()
              .SelectMany(ass => ass.GetTypes())
                //  must be a type that ends with ViewModel
              .Where(type => type != typeof(T) && type.Name.EndsWith("ViewModel"))
                //  must be in a namespace ending with ViewModels
              .Where(type => !(string.IsNullOrWhiteSpace(type.Namespace)) && type.Namespace!=null && type.Namespace.EndsWith("ViewModels"))
                //  must implement INotifyPropertyChanged (deriving from PropertyChangedBase will statisfy this)
              .Where(type => type.GetInterface(typeof(INotifyPropertyChanged).Name, false) != null)
                //  registered as self
              .Apply(a => _container.RegisterPerRequest(a, null, a));

            //  register views
            AssemblySource.Instance.ToArray()
              .SelectMany(ass => ass.GetTypes())
                //  must be a type that ends with View
              .Where(type => type.Name.EndsWith("View"))
                //  must be in a namespace ending with ViewModels
              .Where(type => !(string.IsNullOrWhiteSpace(type.Namespace)) && type.Namespace != null && type.Namespace.EndsWith("Views"))
                //  registered as self
              .Apply(a => _container.RegisterPerRequest(a, null, a));
            
        }
        #endregion
    }
}
#endif